package com.molamil.outboard {
	import com.molamil.outboard.shortcut.KeyboardPositionShortcut;

	import flash.ui.Keyboard;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	import flash.events.MouseEvent;
	import flash.events.KeyboardEvent;
	import flash.events.ProgressEvent;
	import flash.events.IOErrorEvent;
	import flash.events.Event;
	import flash.net.Socket;
	import flash.events.EventDispatcher;
	import flash.display.DisplayObject;

	import com.molamil.outboard.OutboardHelper;

	/**
	 * @author martindyrby
	 */

	public class Outboard extends EventDispatcher {
		
		public static const CONTROL_CHANGE:uint = 176;
		public static const NOTE_ON:uint = 144;
		public static const NOTE_OFF:uint = 128;
		
		public static const TOGGLE : String = "toggle";
		
		private var registry : Array = [];
		private var shortcut : Array = [];
		private var recording : Array;
		
		private var root:DisplayObject;
		private var isRecording : Boolean = false;
		private var timer : Timer;
		private var tmpRecording : Array;
		
		private var services:Array = [];

		public function Outboard(root:DisplayObject) {
			this.root = root;
		}

		public function onKeyUp(key:uint, callback:Function, dispatcher:EventDispatcher = null) : int {
			var oe:KeyEventEntry = KeyEventEntry(createNewEntry(KeyEventEntry));
			oe.keyCode = key;
			oe.dispatcher = (dispatcher) ? dispatcher : root;
			oe.callback = callback;
			oe.type = KeyboardEvent.KEY_UP;
			
			return registerEntry(oe);	
		}

		public function onKeyDown(key:uint, callback:Function, dispatcher:EventDispatcher = null) : int {
			var oe:KeyEventEntry = KeyEventEntry(createNewEntry(KeyEventEntry));
			oe.keyCode = key;
			oe.dispatcher = (dispatcher) ? dispatcher : root;
			oe.callback = callback;
			oe.type = KeyboardEvent.KEY_DOWN;
			
			return registerEntry(oe);	
		}
		
		
		public function adjustPositionWithKeyboard(name:String, target:*, precision:uint = 0) : int {
			
			var kp:KeyboardPositionShortcut = new KeyboardPositionShortcut();
			kp.target = target;
			kp.name = name;
			kp.precision = precision;
			
			return registerShortcut(kp);
		}



		
		
		
		public function createNewEntry(eventEntryClass : Class) : EventEntry {
			var ee:EventEntry = new eventEntryClass();
			ee.recordingListener = recordingListener;
			return ee;
		}

		private function recordingListener(callback:Function, params:Object) : void {
			if(isRecording) {
				recording.push({callback:callback, params: params});
			}
		}

		public function onMouseMove(callback:Function, dispatcher:DisplayObject = null) : int {
			var e:MouseEventEntry = MouseEventEntry(createNewEntry(MouseEventEntry));
			e.dispatcher = (dispatcher) ? dispatcher : root;
			e.callback = callback;
			e.type = MouseEvent.MOUSE_MOVE;
			
			return registerEntry(e);	
		}
		
		public function onMouseDown(callback:Function, dispatcher:DisplayObject = null) : int {
			var e:MouseEventEntry = MouseEventEntry(createNewEntry(MouseEventEntry));
			e.dispatcher = (dispatcher) ? dispatcher : root;
			e.callback = callback;
			e.type = MouseEvent.MOUSE_DOWN;
			
			return registerEntry(e);
		}
		
		public function onMouseUp(callback:Function, dispatcher:DisplayObject = null) : int {
			var e:MouseEventEntry = MouseEventEntry(createNewEntry(MouseEventEntry));
			e.dispatcher = (dispatcher) ? dispatcher : root;
			e.callback = callback;
			e.type = MouseEvent.MOUSE_UP;
			
			return registerEntry(e);
		}
		
		public function registerServer(service:IOutboardService) : void {
			services.push(service);
			service.host = this;
		}

		public function deactivateListener(listenerID:int) : void {
			var ae:AbstractEntry = registry[listenerID];
			ae.destroy();
		}
		
		public function reactivateListener(listenerID:int) : void {
			var ae:AbstractEntry = registry[listenerID];
			ae.init();
		}
		
		public function deactivateShortcut(shortcutID:int) : void {
			var sc:IShortcut = shortcut[shortcutID];
			sc.deactivate();
		}
		
		public function reactivateShortcut(shortcutID:int) : void {
			var sc:IShortcut = shortcut[shortcutID];
			sc.reactivate();
		}

		public function startRecording(setupCallback:Function, params:Object = null) : void {
			recording = [];
			recording[0] = {callback: setupCallback, params: params};
			OutboardHelper.executeMethod(setupCallback, params);
			isRecording = true;
		}

		public function playback() : void {
			tmpRecording = [];	

			timer = new Timer(40, recording.length);
			timer.addEventListener(TimerEvent.TIMER, onTimerTick, false, 0, true);
			timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete, false, 0, true);
			timer.start();
		}

		public function endRecording() : void {
			recording.pop();
			isRecording = false;
		}
		
		public function addDataToLastRecordingEntry(data:Object) : void {
			Object(recording[recording.length - 1]).data = data;
		}
		
		
		
		
		
		
		public function registerEntry(entry:AbstractEntry) : int {
			var regIndex : int = registry.length;
			registry[regIndex] = entry;
			
			entry.init();
			
			return regIndex;
		}
		
		
		public function registerShortcut(entry : IShortcut) : int {
			var scIndex : int = shortcut.length;
			shortcut[scIndex] = entry;
			
			entry.init(this);
			
			return scIndex;
		}
		
		
		

		private function onTimerComplete(event : TimerEvent) : void {
			recording = tmpRecording;
		}

		private function onTimerTick(event : TimerEvent) : void {
			var o:Object = recording.shift();

			if(o != null) {
				if(o.hasOwnProperty("data")) OutboardHelper.executeMethod(o.callback, o.params, o.data);
				else OutboardHelper.executeMethod(o.callback, o.params);
				
				tmpRecording.push(o);
			}
		}
	}
}

class RegistryEntry {
	public var target : Object;
	public var property : Object;
	public var value : Object;

	public function RegistryEntry(target:Object, property:Object, value: Object) {
		this.target = target;
		this.property = property;
		this.value = value;	
	}
}


class IntegerRange {
	public var minValue : int;
	public var maxValue : int;

	public function IntegerRange(minValue:int, maxValue:int) {
		this.minValue = minValue;
		this.maxValue = maxValue;
	}
}


class NumberRange {
	public var minValue : Number;
	public var maxValue : Number;

	public function NumberRange(minValue:Number, maxValue:Number) {
		this.minValue = minValue;
		this.maxValue = maxValue;
	}
}